/*
	Copyright (C) 2010 2011 Rémi Thebault

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <cmath>

#include <QGraphicsScene>
#include <QGraphicsSceneHoverEvent>
#include <QPainter>
#include <QPainterPath>
#include <QPen>

#include <procycle/cycle.h>

#include "procycle.h"
#include "timeaxis.h"
#include "ganttcycle.h"
#include "ganttlink.h"
#include "ganttscene.h"
#include "taskviewhelper.h"
#include "taskselectionmodel.h"


GanttLink::GanttLink(ProCycle* app, GanttCycle* gCycle, PC::Link* link) :
	GanttItem(app, gCycle, gCycle),
	m_link(link),
	m_drawCriticalPath(false),
	m_drawCycleOverlap(false),
	m_paintFromDock(false),
	m_paintToDock(false),
	m_halfStep(0)
{
	m_gtFrom = gCycle->resolvAbsoluteTask(m_link->taskFrom()->tbsId());
	m_gtTo = gCycle->resolvAbsoluteTask(m_link->taskTo()->tbsId());
	setZValue(3);
}


GanttLink::~GanttLink() {
}


void GanttLink::setCycleProp(PC::Cycle::Property p, const QVariant& value) {
	switch (p) {
	case PC::Cycle::PROP_DRAWCRITICAL:
		m_drawCriticalPath = value.toBool();
		break;
	case PC::Cycle::PROP_DRAWOVERLAP:
		m_drawCycleOverlap = value.toBool();
		break;
	case PC::Cycle::PROP_DRAWFLOATS:
	case PC::Cycle::PROP_DRAWTEXT:
		break;
	}
}

void GanttLink::editModeChanged() {
	if (g_editMode == EDIT_TASK) {
		setZValue(2);
	}
	else {
		setZValue(3);
	}
	setAcceptHoverEvents(g_editMode == EDIT_LINK);
}


void GanttLink::syncToExpansion() {
	TaskViewHelper tvhF (m_link->taskFrom()->parent());
	TaskViewHelper tvhT (m_link->taskTo()->parent());
	if(!tvhF.isExpanded() || !tvhT.isExpanded()) {
        hide();
	}
	else {
		show();
	}
}


void GanttLink::requestSelection(bool selected) {
	app()->selectionModel()->selectLink(m_link, selected);
}


QRectF GanttLink::boundingRect() const {
    /*TaskViewHelper tvh (m_link->cycle());
	return QRectF(
			0,
			0,
			scene()->width(),
			app()->rowHeight()*(tvh.visualChildrenHeight())
    );*/
    //return m_bounds;
    // TODO: update link bounds correctly
    return scene()->sceneRect();
}

void GanttLink::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {

	TaskViewHelper tvh (m_link->cycle());
    TimeAxis *axis = app()->axis();

    m_pFrom.setX(static_cast<int>(axis->unit2px(m_link->timeFrom())));
	m_pFrom.setY(app()->rowHeight() * (tvh.visualSubChildPos(m_link->taskFrom())));

    m_pTo.setX(static_cast<int>(axis->unit2px(m_link->timeTo())));
	m_pTo.setY(app()->rowHeight() * (tvh.visualSubChildPos(m_link->taskTo())));

	paintLink(painter);

	if (m_drawCycleOverlap) {
		painter->save();

        int offset = axis->unit2px(m_link->cycle()->length());
		painter->translate(offset, 0);

		paintLink(painter);

		painter->restore();
	}

}


void GanttLink::paintLink(QPainter *painter) {

	m_lines.clear();

	bool rightw = m_pTo.x() > m_pFrom.y();
	bool downw = m_pTo.y() > m_pFrom.y();

	int origToY = m_pTo.y();

	m_pFrom.setY( m_pFrom.y() + (downw ? m_gtFrom->barBottom() : m_gtFrom->barTop() ));
	m_pTo.setY( m_pTo.y() + (downw ? m_gtTo->barTop() : m_gtTo->barBottom()  ));

	m_bounds.setRect(std::min(m_pFrom.x(), m_pTo.x())-3, std::min(m_pFrom.y(), m_pTo.y())-3,
					 std::abs(m_pFrom.x() - m_pTo.x())+6, std::abs(m_pFrom.y() - m_pTo.y())+6);

	bool cp = m_drawCriticalPath && m_link->isCriticalPath();
	QColor arrowC (cp ? Qt::red : Qt::black);
	QColor lagC (cp ? QColor(0xff, 0x99, 0x00) : Qt::green);

	bool straightMode = (rightw && m_link->toStart()) || (!rightw && m_link->toFinish());
	bool horizArrow = straightMode && (m_link->lag()!=0);
	if(horizArrow) {
		m_pTo.setY(origToY + m_gtTo->linkDockY());
	}

	painter->save();

	QPainterPath path (m_pFrom);

	if (straightMode){
		path.lineTo(m_pFrom.x(), m_pTo.y());
		m_lines.append(QLine(m_pFrom.x(), m_pFrom.y(), m_pFrom.x(), m_pTo.y()));
		if (horizArrow) {
			QPainterPath lagPath (QPointF(m_pFrom.x(), m_pTo.y()));
			lagPath.lineTo(m_pTo.x(), m_pTo.y());
			m_lines.append(QLine(m_pFrom.x(), m_pTo.y(), m_pTo.x(), m_pTo.y()));

			QPen pen (lagC);
			pen.setWidth((hovered()||isSelected()) ? 2 : 1);
			painter->setPen(pen);
			painter->drawPath(lagPath);
		}
	}
	else {
		int inter = downw ? m_pTo.y() - 10 : m_pTo.y() + 10;
		path.lineTo(m_pFrom.x(), inter);
		m_lines.append(QLine(m_pFrom.x(), m_pFrom.y(), m_pFrom.x(), inter));

		QPainterPath lagPath (QPointF(m_pFrom.x(), inter));
		lagPath.lineTo(m_pTo.x(), inter);
		m_lines.append(QLine(m_pFrom.x(), inter, m_pTo.x(), inter));

		QPen pen (lagC);
		pen.setWidth((hovered()||isSelected()) ? 2 : 1);
		painter->setPen(pen);
		painter->drawPath(lagPath);

		path.moveTo(m_pTo.x(), inter);
		path.lineTo(m_pTo.x(), m_pTo.y());
		m_lines.append(QLine(m_pTo.x(), inter, m_pTo.x(), m_pTo.y()));
	}

	QPen pen (arrowC);
	pen.setWidth((hovered()||isSelected()) ? 2 : 1);
	painter->setPen(pen);
	painter->setBrush(arrowC);
	painter->drawPath(path);

	QPainterPath arrow (m_pTo);
	if(!horizArrow) {
		if (downw) {
			arrow.lineTo(m_pTo.x()+3, m_pTo.y()-5);
			arrow.lineTo(m_pTo.x()-3, m_pTo.y()-5);
		}
		else {
			arrow.lineTo(m_pTo.x()+3, m_pTo.y()+5);
			arrow.lineTo(m_pTo.x()-3, m_pTo.y()+5);
		}
	}
	else {
		if (rightw) {
			arrow.lineTo(m_pTo.x()-5, m_pTo.y()+3);
			arrow.lineTo(m_pTo.x()-5, m_pTo.y()-3);
		}
		else {
			arrow.lineTo(m_pTo.x()+5, m_pTo.y()+3);
			arrow.lineTo(m_pTo.x()+5, m_pTo.y()-3);
		}
	}
	arrow.closeSubpath();
	painter->drawPath(arrow);

	if(m_paintFromDock) {
		paintFromDock(painter);
	}
	if(m_paintToDock) {
		paintToDock(painter);
	}

	painter->restore();
}


void GanttLink::paintFromDock(QPainter *painter) {
	painter->save();

	painter->setPen(Qt::black);
	painter->setBrush(QColor(0x98, 0xfb, 0x98));

	painter->drawEllipse(m_pFrom, 3, 3);

	painter->restore();
}

void GanttLink::paintToDock(QPainter *painter) {
	painter->save();

	painter->setPen(Qt::black);
	painter->setBrush(QColor(0x98, 0xfb, 0x98));

	painter->drawEllipse(m_pTo, 3, 3);

	painter->restore();
}


bool GanttLink::pointOnLink(const QPointF& p) {
	foreach(QLine l, m_lines) {
		if (    p.x() >= std::min(l.p1().x(), l.p2().x())-3 &&
				p.x() <= std::max(l.p1().x(), l.p2().x())+3 &&
				p.y() >= std::min(l.p1().y(), l.p2().y())-3 &&
				p.y() <= std::max(l.p1().y(), l.p2().y())+3) {
			return true;
		}
	}
	return false;
}

/*bool GanttLink::pointOnFrom(const QPointF& p) {
	return (p.x() >= m_pFrom.x()-3 && p.x() <= m_pFrom.x()+3 &&
			p.y() >= m_pFrom.y()-3 && p.y() <= m_pFrom.y()+3);
}*/

bool GanttLink::pointGrabsLag(const QPointF& p) {
	return (p.x() >= m_pTo.x()-3 && p.x() <= m_pTo.x()+3 &&
			p.y() >= m_pTo.y()-3 && p.y() <= m_pTo.y()+3);
}


void GanttLink::hoverMoveEvent(QGraphicsSceneHoverEvent *event) {
	GanttItem::hoverMoveEvent(event);
	if (g_editMode == EDIT_LINK) {
		if (pointGrabsLag(event->pos())) {
			setCursor(Qt::SizeHorCursor);
		}
		else {
			setCursor(Qt::ArrowCursor);
		}
	}
}


void GanttLink::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) {
	GanttItem::hoverLeaveEvent(event);
	setCursor(Qt::ArrowCursor);
}


void GanttLink::mousePressEvent(QGraphicsSceneMouseEvent *event) {
	if (g_editMode == EDIT_LINK) {
		if (pointGrabsLag(event->pos())) {
			setCursor(Qt::SizeHorCursor);
			g_linkEditMode = LINKEDIT_LAG;
            m_halfStep = app()->axis()->unit2px(1)/2;
			event->accept();
		}
		else {
			setCursor(Qt::ArrowCursor);
			g_linkEditMode = LINKEDIT_NONE;
		}
	}
	if (g_editMode != LINKEDIT_LAG) {
		GanttItem::mousePressEvent(event);
	}
}

void GanttLink::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
	if (g_editMode == EDIT_LINK && g_linkEditMode == LINKEDIT_LAG) {
        int lag = (event->pos().x() - m_pFrom.x()) * app()->axis()->px2unit();
		m_link->setLag(lag);
	}
	else {
		GanttItem::mouseMoveEvent(event);
	}
}

void GanttLink::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
	if (g_editMode == EDIT_LINK && g_linkEditMode == LINKEDIT_LAG) {
		setCursor(Qt::ArrowCursor);
		g_linkEditMode = LINKEDIT_NONE;
	}
	else {
		GanttItem::mouseReleaseEvent(event);
	}
}


